/*
 *  Copyright (c) 2017 Red Hat, Inc. and/or its affiliates.
 *  Copyright (c) 2017 INSA Lyon, CITI Laboratory.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.vertx.guides.wiki;


import com.github.rjeschke.txtmark.Processor;
import io.vertx.core.*;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.ext.auth.PRNG;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CookieHandler;
import io.vertx.ext.web.handler.SessionHandler;

import io.vertx.ext.web.sstore.SessionStore;
import io.vertx.ext.web.sstore.impl.SessionImpl;
import io.vertx.ext.web.templ.FreeMarkerTemplateEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.sstore.LocalSessionStore;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;


/**
 * @author <a href="https://julien.ponge.org/">Julien Ponge</a>
 */
// tag::start[]
public class HttpServerVerticle extends AbstractVerticle {

  private static final Logger LOGGER = LoggerFactory.getLogger(HttpServerVerticle.class);
  public static final String CONFIG_HTTP_SERVER_PORT = "http.server.port";  // <1>
  public static final String CONFIG_WIKIDB_QUEUE = "wikidb.queue";

  private String wikiDbQueue = "wikidb.queue";


  @Override
  public void start(Future<Void> startFuture) throws Exception {

    wikiDbQueue = config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue");  // <2>

    HttpServer server = vertx.createHttpServer();

    SessionStore store = LocalSessionStore.create(vertx, "myapp.sessionmap");
    Router router = Router.router(vertx);

// We need a cookie handler first
    router.route().handler(CookieHandler.create());
    SessionHandler sessionHandler = SessionHandler.create(store);

// Make sure all requests are routed through the session handler too
    router.route().handler(sessionHandler);


    router.get("/").handler(this::indexHandler);
    router.get("/loginPage").handler(this::loginPageHandler);
    router.get("/registerPage").handler(this::registerPageHandler);
    router.get("/wiki/:page").handler(this::pageRenderingHandler);
    router.post().handler(BodyHandler.create());
    router.post("/login").handler(this::loginHandler);
    router.post("/register").handler(this::registerHandler);
    router.post("/save").handler(this::pageUpdateHandler);
    router.post("/create").handler(this::pageCreateHandler);
    router.post("/delete").handler(this::pageDeletionHandler);

    int portNumber = config().getInteger(CONFIG_HTTP_SERVER_PORT, 8080);  // <3>
    server
      .requestHandler(router::accept)
      .

        listen(portNumber, ar ->

        {
          if (ar.succeeded()) {
            LOGGER.info("HTTP server running on port " + portNumber);
            startFuture.complete();
          } else {
            LOGGER.error("Could not start a HTTP server", ar.cause());
            startFuture.fail(ar.cause());
          }
        });
  }


  // (...)
  // end::start[]

  // tag::indexHandler[]
  private final FreeMarkerTemplateEngine templateEngine = FreeMarkerTemplateEngine.create();

  /**
   * 主页面跳转
   *
   * @param context
   */
  private void indexHandler(RoutingContext context) {
    LOGGER.info("hot haode ");
    DeliveryOptions options = new DeliveryOptions().addHeader("action", "all-pages"); // <2>
    String username = context.request().getParam("username");

    vertx.eventBus().send(wikiDbQueue, new JsonObject(), options, reply -> {  // <1>
      if (reply.succeeded()) {
        JsonObject body = (JsonObject) reply.result().body();   // <3>
        context.put("title", "Wiki home");
        context.put("pages", body.getJsonArray("pages").getList());
        context.put("username", username == null ? "" : username);
        Session session = context.session();
        LOGGER.debug("  index");
        LOGGER.debug("sessin :: {}", session);
        LOGGER.debug("sessin userId :: {}", (String) session.get("userId"));
        LOGGER.debug("sessin userName :: {}", (String) session.get("userName"));


        templateEngine.render(context, "templates", "/index.ftl", ar -> {
          if (ar.succeeded()) {
            context.response().putHeader("Content-Type", "text/html");
            context.response().end(ar.result());
          } else {
            context.fail(ar.cause());
          }
        });
      } else {
        context.fail(reply.cause());
      }
    });
  }
  // end::indexHandler[]

  /**
   * 登录页面跳转
   *
   * @param context
   */
  private void loginPageHandler(RoutingContext context) {

    String errMsg = context.request().getParam("errMsg");

    context.put("title", "login page");
    context.put("errMsg", errMsg == null ? "" : errMsg);

    forwardTemplate(context, "templates", "login.ftl");
  }

  /**
   * 注册页面跳转
   *
   * @param context
   */
  private void registerPageHandler(RoutingContext context) {
    String errMsg = context.request().getParam("errMsg");

    context.put("title", "register");
    context.put("errMsg", errMsg == null ? "" : errMsg);

    forwardTemplate(context, "templates", "register.ftl");
  }

  /**
   * 跳转模板页面
   *
   * @param context      路径context
   * @param templatePath 模板所在目录，以resources文件夹为根目录搜索
   * @param templateName 模板名称，需要后缀名
   */
  private void forwardTemplate(RoutingContext context, String templatePath, String templateName) {
    if (null == templatePath) {
      templatePath = "templates";
    }
    templateEngine.render(context, templatePath, "/" + templateName, ar -> {
      if (ar.succeeded()) {
        context.response().putHeader("Content-Type", "text/html; charset=utf-8");
        context.response().end(ar.result());
      } else {
        context.fail(ar.cause());
      }
    });
  }

  /**
   * 登录操作
   *
   * @param context
   */
  private void loginHandler(RoutingContext context) {
    String errMsg = context.request().getParam("errMsg");
    String username = context.request().getParam("username");
    String password = context.request().getParam("password");
    DeliveryOptions options = new DeliveryOptions().addHeader("action", "get-userinfo"); // <2>
    JsonObject body = new JsonObject();
    body.put("username", username);
    body.put("password", password);
    vertx.eventBus().send(wikiDbQueue, body, options, reply -> {  // <1>

      if (reply.succeeded()) {
        JsonObject resultBody = (JsonObject) reply.result().body();

        boolean found = resultBody.getBoolean("found");
        String reulstName = resultBody.getString("username", "");
        String reulstPassword = resultBody.getString("password", "");
        String userId = resultBody.getString("userId");
        context.put("title", "login page");
        context.put("errMsg", errMsg == null ? "" : errMsg);
        LOGGER.debug("login方法");
        if (reulstName.equals(username) && reulstPassword.equals(password)) {
          Session session = context.session();
          session.put("userId", userId);
          session.put("userName", username);
          context.response().setStatusCode(303);
          context.response().putHeader("Location", "/");
          context.response().end();
        } else {
          LOGGER.debug("登录失败");
          context.put("errMsg", "username or password is wrong !!!");
          forwardTemplate(context, null, "login.ftl");
        }
      } else {
        context.fail(reply.cause());
      }

    });

  }

  /**
   * 注册操作
   *
   * @param context
   */
  private void registerHandler(RoutingContext context) {
    //插入userreg userlogin userinfo
    DeliveryOptions userRegOptions = new DeliveryOptions().addHeader("action", "create-userreg");
    DeliveryOptions userLoginOptions = new DeliveryOptions().addHeader("action", "create-userlogin");
    DeliveryOptions userInfoOptions = new DeliveryOptions().addHeader("action", "create-userinfo");
    DeliveryOptions delUserRegOptions = new DeliveryOptions().addHeader("action", "delete-userreg");
    DeliveryOptions delUserLoginOptions = new DeliveryOptions().addHeader("action", "delete-userlogin");
    DeliveryOptions delUserInfoOptions = new DeliveryOptions().addHeader("action", "delete-userinfo");

    String errMsg = context.request().getParam("errMsg");
    String username = context.request().getParam("username");
    String nickname = context.request().getParam("nickname");
    String password = context.request().getParam("password");
    Integer sex = context.request().getParam("sex") == null ? null : Integer.valueOf(context.request().getParam("sex"));


    String guid = UUID.randomUUID().toString().replace("-", "");

    JsonObject userLoginRequest = new JsonObject()
      .put("id", UUID.randomUUID().toString().replace("-", ""))
      .put("sex", sex)
      .put("guid", guid)
      .put("nickname", nickname)
      .put("loginname", username)
      .put("password", password);

    JsonObject userRegRequest = new JsonObject()
      .put("guid", guid)
      .put("sex", sex)
      .put("username", username)
      .put("password", password);

    JsonObject userInfoRequest = new JsonObject()
      .put("id", UUID.randomUUID().toString().replace("-", ""))
      .put("guid", guid)
      .put("nickname", nickname);

    Future<Void> wholeRegFuture = Future.future();
    wholeRegFuture.complete();

    wholeRegFuture.compose(wholeResult ->
    {
      Future<Void> step = Future.future();
      step.complete();
      step.compose(regResult -> {
        Future<Void> userRegFuture = Future.future();

        LOGGER.debug("userRegFuture  ...");
        vertx.eventBus().send(wikiDbQueue, userRegRequest, userRegOptions, regReply -> {
            LOGGER.debug("userRegFuture  ...   sql is done");
            userRegFuture.complete();
          }
        );

        userRegFuture.compose(loginResult -> {
          Future<Void> userLoginFuture = Future.future();

          LOGGER.debug("userLoginFuture  ...");
          vertx.eventBus().send(wikiDbQueue, userLoginRequest, userLoginOptions, loginReply -> {
            LOGGER.debug("userLoginFuture  ...   sql is done");
            userLoginFuture.complete();
          });

          userLoginFuture.compose(userInfoResult -> {
            Future<Void> userInfoFuture = Future.future();


            LOGGER.debug("userInfoFuture  ...");
            vertx.eventBus().send(wikiDbQueue, userInfoRequest, userInfoOptions, userInfoReply -> {
              LOGGER.debug("userInfoFuture  ...   sql is done");
              userInfoFuture.complete();
            });
            //最后一步
            userInfoFuture.setHandler(handler -> {
              if (handler.succeeded()) {
                System.out.println(handler.result());
                //跳转到登录页
                context.put("title", "register...");
                context.response().setStatusCode(303);
                context.response().putHeader("Location", "/loginPage");
                context.response().end();
//              userInfoFuture.completer();

              } else {
                // Manage doSmthg3 errors
              }
            });

          }, Future.future().setHandler(handler -> {


          }));

        }, Future.future().setHandler(handler -> {

        }));

      }, Future.future().setHandler(handler -> { // userinfo “事务补偿”  删除用户信息数据
        vertx.eventBus().send(wikiDbQueue, userInfoRequest, delUserRegOptions, reply -> {
          LOGGER.debug("delUserRegOptions  ...   sql is done");
        });
        // userlogin “事务补偿”  删除用户登录数据
        vertx.eventBus().send(wikiDbQueue, userLoginRequest, delUserLoginOptions, reply -> {
          LOGGER.debug("delUserLoginOptions  ...   sql is done");
        });
        // userreg “事务补偿”  删除用户注册数据
        vertx.eventBus().send(wikiDbQueue, userRegRequest, delUserRegOptions, reply -> {
          LOGGER.debug("delUserRegOptions  ...   sql is done");
        });


      }));
    }, Future.future().setHandler(handler -> {

    }));


  }


  /**
   * 303重定向，默认重定向  /
   *
   * @param context
   * @param replyHandler
   * @param location
   */
  private void redirect(RoutingContext context, AsyncResult<Message<Object>> replyHandler, String location) {
    if (null == location) {
      location = "/";
    }
    if (replyHandler.succeeded()) {
      context.response().setStatusCode(303);
      context.response().putHeader("Location", location);
      context.response().end();
    } else {
      context.fail(replyHandler.cause());
    }
  }

  // tag::rest[]
  private static final String EMPTY_PAGE_MARKDOWN =
    "# A new page\n" +
      "\n" +
      "Feel-free to write in Markdown!\n";

  /**
   * 页面解析
   *
   * @param context
   */
  private void pageRenderingHandler(RoutingContext context) {

    String requestedPage = context.request().getParam("page");
    JsonObject request = new JsonObject().put("page", requestedPage);

    DeliveryOptions options = new DeliveryOptions().addHeader("action", "get-page");
    vertx.eventBus().send(wikiDbQueue, request, options, reply -> {

      if (reply.succeeded()) {
        JsonObject body = (JsonObject) reply.result().body();

        boolean found = body.getBoolean("found");
        String rawContent = body.getString("rawContent", EMPTY_PAGE_MARKDOWN);
        context.put("title", requestedPage);
        context.put("id", body.getInteger("id", -1));
        context.put("newPage", found ? "no" : "yes");
        context.put("rawContent", rawContent);
        context.put("content", Processor.process(rawContent));
        context.put("timestamp", new Date().toString());

        templateEngine.render(context, "templates", "/page.ftl", ar -> {
          if (ar.succeeded()) {
            context.response().putHeader("Content-Type", "text/html");
            context.response().end(ar.result());
          } else {
            context.fail(ar.cause());
          }
        });

      } else {
        context.fail(reply.cause());
      }
    });
  }

  /**
   * 页面更新操作
   *
   * @param context
   */
  private void pageUpdateHandler(RoutingContext context) {

    String title = context.request().getParam("title");
    JsonObject request = new JsonObject()
      .put("id", context.request().getParam("id"))
      .put("title", title)
      .put("markdown", context.request().getParam("markdown"));

    DeliveryOptions options = new DeliveryOptions();
    if ("yes".equals(context.request().getParam("newPage"))) {
      options.addHeader("action", "create-page");
    } else {
      options.addHeader("action", "save-page");
    }

    vertx.eventBus().send(wikiDbQueue, request, options, reply -> {
      if (reply.succeeded()) {
        context.response().setStatusCode(303);
        context.response().putHeader("Location", "/wiki/" + title);
        context.response().end();
      } else {
        context.fail(reply.cause());
      }
    });
  }

  /**
   * 创建page
   *
   * @param context
   */
  private void pageCreateHandler(RoutingContext context) {
    String pageName = context.request().getParam("name");
    String location = "/wiki/" + pageName;
    if (pageName == null || pageName.isEmpty()) {
      location = "/";
    }
    context.response().setStatusCode(303);
    context.response().putHeader("Location", location);
    context.response().end();
  }

  /**
   * 删除页面
   *
   * @param context
   */
  private void pageDeletionHandler(RoutingContext context) {
    String id = context.request().getParam("id");
    JsonObject request = new JsonObject().put("id", id);
    DeliveryOptions options = new DeliveryOptions().addHeader("action", "delete-page");
    vertx.eventBus().send(wikiDbQueue, request, options, reply -> {
      if (reply.succeeded()) {
        context.response().setStatusCode(303);
        context.response().putHeader("Location", "/");
        context.response().end();
      } else {
        context.fail(reply.cause());
      }
    });
  }


  // end::rest[]
}
